package org.report.word;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.ChartMultiSeriesRenderData;
import com.deepoove.poi.data.Charts;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.policy.DynamicTableRenderPolicy;
import com.deepoove.poi.policy.RenderPolicy;
import com.deepoove.poi.util.TableTools;
import org.apache.poi.xwpf.usermodel.*;
import org.report.enums.WordChartEnum;
import org.report.util.FileUtils;
import org.report.word.dto.*;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.*;

/**
 * 生成word文档
 * 参考文档地址：https://deepoove.com/poi-tl/1.9.x/#plugin-list
 *
 * @author Administrator
 * @describe 说明
 * @date 2024/5/8 0008 11:56
 */
public class GenerateWord {

    /**
     * 填充word文档数据
     *
     * @param file
     * @return
     */
    public static File fillWord(File file, ParamDto param) {
        ConfigureBuilder builder = Configure.builder();
        if (!CollectionUtils.isEmpty(param.getOrdinaryTableMap())) {
            param.getOrdinaryData().putAll(param.getOrdinaryTableMap());
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            for (Map.Entry<String, List<Map<String, Object>>> entry : param.getOrdinaryTableMap().entrySet()) {
                builder.bind(entry.getKey(), policy);
            }
        }

        if (!isEmpty(param.getDynamicTableRenderDtoList())) {
            for (DynamicTableRenderDto dynamicTableRenderDto : param.getDynamicTableRenderDtoList()) {
                builder.bind(dynamicTableRenderDto.getTableKey(), new DynamicTableRenderPolicy() {
                    @Override
                    public void render(XWPFTable table, Object data) throws Exception {
                        if (isEmpty(dynamicTableRenderDto.getRowList())) {
                            return;
                        }
                        // 开始处理的行数据
                        Integer startRow = dynamicTableRenderDto.getStartRow();
                        // 获取要复制的行
                        XWPFTableRow row = table.getRow(startRow);
                        // 循环插入行
                        for (Map<String, Object> objectMap : dynamicTableRenderDto.getRowList()) {
                            // 从startRow行开始插入
                            XWPFTableRow insertNewTableRow = table.insertNewTableRow(startRow++);
                            // 复制行的样式给新行
                            insertNewTableRow.getCtRow().setTrPr(row.getCtRow().getTrPr());
                            // 创建单元格，并写入数据
                            for (XWPFTableCell tableCell : row.getTableCells()) {
                                String key = tableCell.getText().replace("[", "").replace("]", "");
                                // 复制单元格的样式给新的单元格
                                XWPFTableCell cell = insertNewTableRow.addNewTableCell();
                                cell.getCTTc().setTcPr(tableCell.getCTTc().getTcPr());
                                cell.setText(String.valueOf(objectMap.getOrDefault(key, "")));
                            }
                        }
                        // 合并单元格
                        if (!isEmpty(dynamicTableRenderDto.getMergeTableCellDtoList())) {
                            for (MergeTableCellDto mergeTableCellDto : dynamicTableRenderDto.getMergeTableCellDtoList()) {
                                if (mergeTableCellDto.getType() == 1) {
                                    // 横向
                                    TableTools.mergeCellsHorizonal(table, mergeTableCellDto.getStart(), mergeTableCellDto.getFrom(), mergeTableCellDto.getTo());
                                } else {
                                    // 纵向
                                    TableTools.mergeCellsVertically(table, mergeTableCellDto.getStart(), mergeTableCellDto.getFrom(), mergeTableCellDto.getTo());
                                }
                            }
                        }
                        // 移除模板行
                        table.removeRow(startRow);
                    }
                });
            }
        }

        if (!isEmpty(param.getCustomRenderPolicyMap())) {
            for (Map.Entry<String, RenderPolicy> policyEntry : param.getCustomRenderPolicyMap().entrySet()) {
                builder.bind(policyEntry.getKey(), policyEntry.getValue());
            }
        }
        if (!isEmpty(param.getChartDataDtoList())) {
            buildChartData(param.getOrdinaryData(), param.getChartDataDtoList());
        }

        try (XWPFTemplate template = XWPFTemplate.compile(file, builder.build()).render(param.getOrdinaryData());
             FileOutputStream fos = new FileOutputStream(file)) {
            template.write(fos);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
        if (!isEmpty(param.getMergeTableCellMap())) {
            try (FileInputStream fis = new FileInputStream(file);
                 XWPFDocument doc = new XWPFDocument(fis);
                 FileOutputStream fos = new FileOutputStream(file)
            ) {
                for (Map.Entry<Integer, List<MergeTableCellDto>> entry : param.getMergeTableCellMap().entrySet()) {
                    XWPFTable table = doc.getTables().get(entry.getKey());
                    for (MergeTableCellDto mergeTableCellDto : entry.getValue()) {
                        if (mergeTableCellDto.getType() == 1) {
                            // 横向
                            TableTools.mergeCellsHorizonal(table, mergeTableCellDto.getStart(), mergeTableCellDto.getFrom(), mergeTableCellDto.getTo());
                        } else {
                            // 纵向
                            TableTools.mergeCellsVertically(table, mergeTableCellDto.getStart(), mergeTableCellDto.getFrom(), mergeTableCellDto.getTo());
                        }
                    }
                }
                doc.write(fos);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }
        return file;
    }

    /**
     * 构建图表数据
     *
     * @param ordinaryData     存储图表key与对应构建好的图表内容
     * @param chartDataDtoList 图表数据
     */
    private static void buildChartData(Map<String, Object> ordinaryData, List<ChartDataDto> chartDataDtoList) {
        for (ChartDataDto chartDataDto : chartDataDtoList) {
            switch (chartDataDto.getChartType()) {
                case Pie -> {
                    PieChartDataDto pieChartDataDto = (PieChartDataDto) chartDataDto;
                    ordinaryData.put(pieChartDataDto.getKey(),
                            Charts.ofPie(pieChartDataDto.getTitle(), pieChartDataDto.getCategories())
                                    .series("", pieChartDataDto.getSeries()).create());
                }
                case Pie3D -> {
                    PieChartDataDto pieChartDataDto = (PieChartDataDto) chartDataDto;
                    ordinaryData.put(pieChartDataDto.getKey(),
                            Charts.ofPie3D(pieChartDataDto.getTitle(), pieChartDataDto.getCategories())
                                    .series("", pieChartDataDto.getSeries()).create());
                }
                case Doughnut -> {
                    PieChartDataDto pieChartDataDto = (PieChartDataDto) chartDataDto;
                    ordinaryData.put(pieChartDataDto.getKey(),
                            Charts.ofDoughnut(pieChartDataDto.getTitle(), pieChartDataDto.getCategories())
                                    .series("", pieChartDataDto.getSeries()).create());
                }
                case Bar -> {
                    BarChartDataDto barChartDataDto = (BarChartDataDto) chartDataDto;
                    Charts.ChartMultis chartMultis = Charts.ofBar(barChartDataDto.getTitle(), barChartDataDto.getCategories());
                    for (Map.Entry<String, Number[]> entry : barChartDataDto.getSeriesMap().entrySet()) {
                        chartMultis.addSeries(entry.getKey(), entry.getValue());
                    }
                    ordinaryData.put(barChartDataDto.getKey(), chartMultis.create());
                }
                case Bar3D -> {
                    BarChartDataDto barChartDataDto = (BarChartDataDto) chartDataDto;
                    Charts.ChartMultis chartMultis = Charts.ofBar3D(barChartDataDto.getTitle(), barChartDataDto.getCategories());
                    for (Map.Entry<String, Number[]> entry : barChartDataDto.getSeriesMap().entrySet()) {
                        chartMultis.addSeries(entry.getKey(), entry.getValue());
                    }
                    ordinaryData.put(barChartDataDto.getKey(), chartMultis.create());
                }
                case Line -> {
                    LineChartDataDto lineChartDataDto = (LineChartDataDto) chartDataDto;
                    Charts.ChartMultis chartMultis = Charts.ofLine(lineChartDataDto.getTitle(), lineChartDataDto.getCategories());
                    for (Map.Entry<String, Number[]> entry : lineChartDataDto.getSeriesMap().entrySet()) {
                        chartMultis.addSeries(entry.getKey(), entry.getValue());
                    }
                    ordinaryData.put(lineChartDataDto.getKey(), chartMultis.create());
                }
                case Line3D -> {
                    LineChartDataDto lineChartDataDto = (LineChartDataDto) chartDataDto;
                    Charts.ChartMultis chartMultis = Charts.ofLine3D(lineChartDataDto.getTitle(), lineChartDataDto.getCategories());
                    for (Map.Entry<String, Number[]> entry : lineChartDataDto.getSeriesMap().entrySet()) {
                        chartMultis.addSeries(entry.getKey(), entry.getValue());
                    }
                    ordinaryData.put(lineChartDataDto.getKey(), chartMultis.create());
                }
                case Combo -> {
                    ComboChartDataDto comboChartDataDto = (ComboChartDataDto) chartDataDto;
                    ordinaryData.put(comboChartDataDto.getKey(), buildComboChartData(comboChartDataDto));
                }
            }
        }
    }

    /**
     * 构建组合类型的图表数据
     *
     * @param comboChartDataDto 组合类型的图表数据
     * @return
     */
    private static ChartMultiSeriesRenderData buildComboChartData(ComboChartDataDto comboChartDataDto) {
        Charts.ChartCombos chartCombos = Charts.ofComboSeries(comboChartDataDto.getTitle(), comboChartDataDto.getCategories());
        for (Map.Entry<WordChartEnum, Map<String, Number[]>> wordChartEnumMapEntry : comboChartDataDto.getSeriesMap().entrySet()) {
            switch (wordChartEnumMapEntry.getKey()) {
                case Bar -> {
                    for (Map.Entry<String, Number[]> entry : wordChartEnumMapEntry.getValue().entrySet()) {
                        chartCombos.addBarSeries(entry.getKey(), entry.getValue());
                    }
                }
                case Line -> {
                    for (Map.Entry<String, Number[]> entry : wordChartEnumMapEntry.getValue().entrySet()) {
                        chartCombos.addLineSeries(entry.getKey(), entry.getValue());
                    }
                }
                default ->
                        throw new RuntimeException("组合图中，赞不支持[" + wordChartEnumMapEntry.getKey().getDesc() + "]");
            }
        }
        return chartCombos.create();
    }

    public static boolean isEmpty(Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof Collection collection) {
            return collection.size() < 1;
        } else if (obj instanceof Map map) {
            return map.size() < 1;
        }
        return false;
    }

    public static void main(String[] args) {
        ParamDto param = new ParamDto();
        File file = FileUtils.copyResourceFile("word/test-template.docx", ".docx");

        /* 饼状图表填充 =====================开始====================== */
        PieChartDataDto pieChartDataDto = new PieChartDataDto();
        pieChartDataDto.setChartType(WordChartEnum.Pie).setTitle("XX占比")
                .setCategories(new String[]{"模块1", "模块2", "模块3", "模块4"});
        pieChartDataDto.setSeries(new Number[]{0.15, 0.25, 0.15, 0.45})
                .setKey("chart_yszb");
        param.getChartDataDtoList().add(pieChartDataDto);
        pieChartDataDto = new PieChartDataDto();
        pieChartDataDto.setChartType(WordChartEnum.Pie).setTitle("YY占比")
                .setCategories(new String[]{"yy1", "yy2", "yy3", "yy4"});
        pieChartDataDto.setSeries(new Number[]{0.15, 0.20, 0.15, 0.5})
                .setKey("chart_mlrzb");
        param.getChartDataDtoList().add(pieChartDataDto);
        /* 饼状图表填充 =====================结束====================== */
        /* 柱状图表填充 =====================开始====================== */
        BarChartDataDto barChartDataDto = new BarChartDataDto();
        barChartDataDto.setChartType(WordChartEnum.Bar).setCategories(new String[]{"测试1", "测试2", "测试3"})
                .setTitle("柱状图表测试").setKey("chart_aa");
        Map<String, Number[]> seriesMap = new HashMap<>();
        seriesMap.put("类别1", new Number[]{15, 20, 67});
        seriesMap.put("类别fdas", new Number[]{63, 20, 67});
        seriesMap.put("类别3", new Number[]{15, 5, 67});
        barChartDataDto.setSeriesMap(seriesMap);
        param.getChartDataDtoList().add(barChartDataDto);
        /* 柱状图表填充 =====================开始====================== */
        /* 柱折线图表填充 =====================开始====================== */
        LineChartDataDto lineChartDataDto = new LineChartDataDto();
        lineChartDataDto.setChartType(WordChartEnum.Line).setCategories(new String[]{"测试1", "测试2", "测试3"})
                .setTitle("柱状图表测试").setKey("chart_zx");
        seriesMap = new HashMap<>();
        seriesMap.put("类别1", new Number[]{15, 20, 67});
        seriesMap.put("类别fdas", new Number[]{63, 20, 67});
        seriesMap.put("类别3", new Number[]{15, 5, 67});
        lineChartDataDto.setSeriesMap(seriesMap);
        param.getChartDataDtoList().add(lineChartDataDto);
        /* 柱折线图表填充 =====================开始====================== */

        /* 基础类型填充 =====================开始====================== */
        Map<String, Object> data = new HashMap<>();
        data.put("year", "2024");
        data.put("month", "05");
        data.put("generateDate", "2024年05月05日");
        data.put("t1_2_org_orgNum", "38");
        data.put("t1_2_org_realNum", "23");
        param.setOrdinaryData(data);
        /* 基础类型填充 =====================结束====================== */


        // 普通的table
        /* 普通的table =====================开始====================== */
        // 第一个表格
        List<Map<String, Object>> tableList = new ArrayList<>();
        data = new HashMap<>();
        data.put("group", "一、单位1");
        data.put("orgNum", "10");
        data.put("realNum", "8");
        tableList.add(data);
        data = new HashMap<>();

        data.put("group", "二、单位2");
        data.put("orgNum", "15");
        data.put("realNum", "9");
        tableList.add(data);

        data = new HashMap<>();
        data.put("group", "三、单位3");
        data.put("orgNum", "13");
        data.put("realNum", "6");
        tableList.add(data);
        Map<String, List<Map<String, Object>>> ordinaryTableMap = new HashMap<>();
        ordinaryTableMap.put("t1_2_org", tableList);

        // 第二个表格
        List<Map<String, Object>> list = new ArrayList<>();
        List<String> orgList = Arrays.asList("公司1", "公司2", "公司3");
        int i = 1;
        for (String org : orgList) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("group", "一、组织1");
            map.put("orgNum", "3");
            map.put("index", i);
            map.put("org", org);
            list.add(map);
            i++;
        }

        orgList = Arrays.asList("公司4", "公司5");
        for (String org : orgList) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("group", "二、组织2");
            map.put("orgNum", "2");
            map.put("index", i);
            map.put("org", org);
            list.add(map);
            i++;
        }

        Map<String, Object> map = new LinkedHashMap<>();
        map.put("group", "三、组织3");
        map.put("orgNum", "0");
        map.put("index", "");
        map.put("org", "");
        list.add(map);
        ordinaryTableMap.put("t_org_merge", list);

        // 第三个表格
        list = new ArrayList<>();
        for (int j = 0; j < 10; j++) {
            map = new LinkedHashMap<>();
            map.put("group", "组织");
            map.put("orgNum", "1" + j);
            map.put("index", "" + j);
            map.put("org", "公司" + j);
            list.add(map);
        }
        ordinaryTableMap.put("orgList2", list);
        param.setOrdinaryTableMap(ordinaryTableMap);
        /* 普通的table =====================结束====================== */

        /* 动态table填充（含合并） =====================开始====================== */
        list = new ArrayList<>();
        orgList = Arrays.asList("公司1", "公司2", "公司3");
        i = 1;
        for (String org : orgList) {
            map = new LinkedHashMap<>();
            map.put("group", "一、组织1");
            map.put("orgNum", "3");
            map.put("index", i);
            map.put("org", org);
            list.add(map);
            i++;
        }

        orgList = Arrays.asList("公司4", "公司5");
        for (String org : orgList) {
            map = new LinkedHashMap<>();
            map.put("group", "二、组织2");
            map.put("orgNum", "2");
            map.put("index", i);
            map.put("org", org);
            list.add(map);
            i++;
        }

        map = new LinkedHashMap<>();
        map.put("group", "三、组织3");
        map.put("orgNum", "0");
        map.put("index", "");
        map.put("org", "");
        list.add(map);

        List<DynamicTableRenderDto> renderDTOS = new ArrayList<>();
        DynamicTableRenderDto dto = new DynamicTableRenderDto();
        dto.setStartRow(1);
        dto.setRowList(list);
        dto.setTableKey("orgList");
        // 合并单元格
        List<MergeTableCellDto> mergeTableCellDTOList = new ArrayList<>();
        MergeTableCellDto cellDTO = new MergeTableCellDto();
        cellDTO.setType(2);
        cellDTO.setStart(0);
        cellDTO.setFrom(4);
        cellDTO.setTo(5);
        mergeTableCellDTOList.add(cellDTO);
        cellDTO = new MergeTableCellDto();
        cellDTO.setType(2);
        cellDTO.setStart(0);
        cellDTO.setFrom(1);
        cellDTO.setTo(3);
        mergeTableCellDTOList.add(cellDTO);
        dto.setMergeTableCellDtoList(mergeTableCellDTOList);
        renderDTOS.add(dto);
        param.setDynamicTableRenderDtoList(renderDTOS);
        /* 动态table填充（含合并） =====================结束====================== */

        /* 合并单元格 =====================开始====================== */
        mergeTableCellDTOList = new ArrayList<>();
        cellDTO = new MergeTableCellDto();
        cellDTO.setType(2);
        cellDTO.setStart(0);
        cellDTO.setFrom(4);
        cellDTO.setTo(5);
        mergeTableCellDTOList.add(cellDTO);
        cellDTO = new MergeTableCellDto();
        cellDTO.setType(2);
        cellDTO.setStart(0);
        cellDTO.setFrom(1);
        cellDTO.setTo(3);
        mergeTableCellDTOList.add(cellDTO);
        param.getMergeTableCellMap().put(3, mergeTableCellDTOList);
        /* 合并单元格 =====================结束====================== */

        fillWord(file, param);

        System.out.println("文件路径：" + file.getAbsolutePath());
    }
}
